package org.jfonia.musicxml;

import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.sun.org.apache.xerces.internal.parsers.DOMParser;
import com.sun.org.apache.xerces.internal.xni.Augmentations;
import com.sun.org.apache.xerces.internal.xni.NamespaceContext;
import com.sun.org.apache.xerces.internal.xni.QName;
import com.sun.org.apache.xerces.internal.xni.XMLAttributes;
import com.sun.org.apache.xerces.internal.xni.XMLLocator;
import com.sun.org.apache.xerces.internal.xni.XNIException;

/*
 * Helper class for parsing XML to DOM.
 * 
 * Adapted from 
 * http://svn.apache.org/viewvc/xerces/java/trunk/samples/dom/DOMAddLines.java?revision=320323&view=markup
 * 
 * @author wijnand.schepens@gmail.com
 **/
public class SimpleDOMParser extends DOMParser
{

	private XMLLocator	locator;

	public SimpleDOMParser() throws SAXException
	{
		// fNodeExpansion = FULL; // faster than:
		// this.setFeature("http://apache.org/xml/features/defer-node-expansion",
		// false);

		this.setFeature("http://apache.org/xml/features/dom/defer-node-expansion", false);
		// needed in order to query current element
	}

	// override
	public void startElement(QName elementQName, XMLAttributes attrList, Augmentations augs) throws XNIException
	{

		super.startElement(elementQName, attrList, augs);

		try
		{
			Node node = (Node) this.getProperty("http://apache.org/xml/properties/dom/current-element-node");
			if (node != null)
				node.setUserData("startLine", String.valueOf(locator.getLineNumber()), null); // Save location String into
			// node
		}
		catch (org.xml.sax.SAXException ex)
		{
			throw new XNIException(ex); // wijnand.schepens
		}
	}

	// override:
	public void startDocument(XMLLocator locator, String encoding, NamespaceContext namespaceContext, Augmentations augs) throws XNIException
	{
		super.startDocument(locator, encoding, namespaceContext, augs);

		this.locator = locator;

		try
		{
			Node node = (Node) this.getProperty("http://apache.org/xml/properties/dom/current-element-node");
			if (node != null)
				node.setUserData("startLine", String.valueOf(locator.getLineNumber()), null);
		}
		catch (org.xml.sax.SAXException ex)
		{
			throw new XNIException(ex); // wijnand.schepens
		}
	}

	public static String getStartLine(Node node)
	{
		return (String) node.getUserData("startLine");
	}

	public static Node findChild(Node parent, String name)
	{
		NodeList list = parent.getChildNodes();
		int n = list.getLength();
		int i = 0;
		while (i < n && !list.item(i).getNodeName().equals(name))
			i++;
		return i < n ? list.item(i) : null;
	}

	public static String findAttribute(Node node, String name)
	{
		NamedNodeMap map = node.getAttributes();
		if (map == null)
			return null;
		Node item = map.getNamedItem(name);
		return item != null ? item.getNodeValue() : null;
	}

	public static Node findChildWithAttribute(Node parent, String name, String attrName, String requestedAttrValue)
	{
		NodeList list = parent.getChildNodes();
		for (int i = 0; i < list.getLength(); i++)
		{
			Node node = list.item(i);
			if (node.getNodeName().equals(name))
			{
				String attrValue = findAttribute(node, attrName);
				if (attrValue != null && attrValue.equals(requestedAttrValue))
					return node;
			}
		}
		return null;
	}

	public static int parseToInt(String s, Node refNode) throws CodedMusicXMLImportException
	{
		try
		{
			return Integer.parseInt(s);
		}
		catch (NumberFormatException e)
		{
			throw new CodedMusicXMLImportException("INT", "instead of '"+ s + "'", refNode);
		}
	}

	public static int parseTextContentToInt(Node refNode) throws CodedMusicXMLImportException
	{
		return parseToInt(refNode.getTextContent(), refNode);
	}

	public static int parseAttributeToInt(Node refNode, String attrName) throws CodedMusicXMLImportException
	{
		String attrValue = findAttribute(refNode, attrName);
		if (attrValue == null)
			throw new CodedMusicXMLImportException("ATTR", attrName, refNode);
		return parseToInt(attrValue, refNode);
	}

}
